// Copyright 2009 Vladimir Solomenchuk
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

dojo.provide("judo.zone");

dojo.require("dojo._base.xhr");
dojo.require("judo.base");
dojo.require("judo.events");
dojo.require("dijit._base.manager");

judo.zone=new function(){
	this.zonePrefix=".jz-";
	this.srcPrefix=".jzs";
	var reZoneSourceId=/jzs-(\S+)/;
	var _connects=[];
	var _events={};
	var _globalEvents={};
	var reValidationErrorClass=/class=(['"])*?[^>]*?t-error\1/i;

	function fire(zoneId,event,args)
	{
		try
		{
			var ehandle=_events[zoneId];
			if (ehandle && ehandle[event])
				ehandle[event].apply(this,args);
			
			ehandle=_globalEvents;
			if (ehandle && ehandle[event])
				ehandle[event].apply(this,args);
		}
		catch(e)
		{
			if (dojo.config.isDebug) 
				console.error(e.message, e);
			else
				alert(e.message);
		}
	}
	
	function errorHandler(e, ioArgs)
	{
		if (e && e.dojoType == "cancel")
			return;
		if (dojo.config.isDebug) {
			console.error(ioArgs);
			alert("judo.zone ioError:"+e.message);
		}
		if (ioArgs.args && ioArgs.args.form)
			ioArgs.args.form.submit();
		else
			window.location.href=ioArgs.url;
	};

	function successHandler(/*html*/ data, /* domNode | widget*/ zone, /* String */ zoneId)
	{
		if (dojo.config.isDebug) {
			console.debug(data);
		}
		if (!data) //no content
			return;
		var zones={};
		
		try
		{
			var sData=dojo.fromJson(data);

			//just redirect
			if (typeof sData == 'xml')
				sData = sData.toString();
			
			if (typeof sData=="object")
			{
				if (sData["redirectURL"])
				{
					window.location.href=sData["redirectURL"];
					return;
				}

				if (sData["zones"]) //multiple zones
				{
					var zz=sData["zones"];
					for(var zId in zz)
					{
						var tz=judo.zone.byId(zId);
						if (!tz)
							throw new Error('unable to find zone: '+zId);
						var tr={};
						zones[zId]=zz[zId];
					}
				}
				else if (typeof sData["content"] != 'undefined')
				{
					zones[zoneId]=sData["content"];
				}
				else
				{
					var emptyContent=true; //check if empty object
					for(var i in sData)
					{
						emptyContent=false;
						break;
					}
					zones[zoneId]=emptyContent?"":sData.toString();
				}
			}
			else
			{
				zones[zoneId]=sData.toString();
			}
		}
		catch(e)
		{
			zones[zoneId]=data.toString();
		}
		for (var zid in zones)
		{
			var z=judo.zone.byId(zid);
			if (zid!==zoneId)
				fire(zoneId,"onZoneBegin",[zoneId, zone]);
			
			if (z)
			{
				var content=zones[zid];
				unregisterZoneSources(z);
				if (z.attr)
					z.attr("content",content);
				else if (typeof zone.innerHTML!="undefined")
					z.innerHTML=content;

				else
					throw new Error("unable to set content");
				
				judo.zone.scan(z);

				if (reValidationErrorClass.exec(content))
					fire(zoneId,"onZoneValidationError",[z, data]);
				else
					fire(zoneId,"onZoneSuccess",[z, data]);
				fire(zoneId,"onZoneEnd",[z, data]);

			}
		}
	};
	
	this.byId=function(/* STring */ zoneId)
	{
		// summary:
		//		returns zone node or widget, null if not found
		
		//find target zone.
		//FIXME
		//For some reason, Tapestry sometimes (when we are trying to kake client id from not rendered yet component) 
		//assigns wrong client ids
		var zid1;
		var zid2;
		if (zoneId.length>2 && zoneId.substring(zoneId.length-2)=="_0")
		{
			zid1=zoneId.substring(0,zoneId.length-2);
			zid2=zoneId;
		}
		else
		{
			zid1=zoneId;
			zid2=zoneId+"_0";
		}
		
		var nodes=dojo.query(this.zonePrefix+zid1).forEach(function(node, index, arr){zone=node});
		
		if (nodes.length==0)
			nodes=dojo.query(this.zonePrefix+zid2).forEach(function(node, index, arr){zone=node});
		
		if (!zone)
			throw new Error("unable to find zone "+zoneId);
		
		return dijit.byNode(zone) || zone; //is zone widget?
	};
	
	function unregisterZoneSources(zone)
	{
		var rootNode=zone.domNode || zone;
		dojo.query(this.srcPrefix, rootNode).forEach(function(node, index, arr){
			var evt=findConnectByNode(node);
			if (evt && evt.c.n===node)
			{
				console.log(evt)
				node.__orgEvent=null;
				dojo.disconnect(evt.c.e);
				_connects.splice(evt.i,1);
			}
		 });
	};
	
	function event(e){
		var source=e.currentTarget;
		//cancel event, if original event stops
		try
		{
			if (dojo.isFunction(source.__orgEvent))
			{
				if (source.__orgEvent(e)===false)
				{
					dojo.stopEvent(e);
					return false;
				}
			}
		}
		catch(err) //if original event fails - report it in debug mode invoke non-ajax request
		{
			if (dojo.config.isDebug)
				alert("judo.zone eventError:"+err.message);
			return true;
		}
		
		//find source id
		var m = reZoneSourceId.exec(source.className);
		if (m==null)
		{
			if (dojo.config.isDebug)
                alert("judo.zone: invalid css class: "+source.className);
            return true;
		}
		var zoneId=m[1];
		var zone=this.byId(zoneId);
		
		fire(zoneId,"onZoneBegin",[zoneId, zone]);

		//set common properties
		var kv={
			handleAs: 'text',
			load:function(data){
				successHandler(data, zone, zoneId);
			},
			error:errorHandler
		}
		if (source.tagName=="FORM") //in case of source is form, we need set form attribute and url from form action
		{
			kv.url = source.getAttribute("action");
			kv.form = source;
		}
		else
		{
			kv.url = source.href;
			if (!kv.url) //clickable widget with href attr?
			{
				var widget=dijit.byNode(source);
				kv.url=widget.attr("href");
			}
		}
		if (!kv.url)
		{
			var errMsg="judo.zone eventError: unable to define url";
			if (dojo.config.isDebug)
				alert(errMsg);
			throw new Error(errMsg);
		}
		
		//stop event from bubbling
		dojo.xhrPost(kv);
		dojo.stopEvent(e);
		return false;
	};
	
	this.scan=function(zone){
		if (!zone)
			zone=dojo.body();
		var rootNode=zone.domNode || zone;
		var self=this;
		//find all targets
		dojo.query(this.srcPrefix,rootNode).forEach(function(node, index, arr){
			//we will override events instead of attach - if one of the original event(s) will fail - we will prevent execution
			var evt;
			//prevet dublicate connects
			if (findConnectByNode(node))
			{
				return;
			}
			
			switch (node.tagName)
			{
				case "FORM":
					var formWidget=dijit.byNode(node);
					if (formWidget) //handle dijit.form.Form in special way
					{
						node.__orgEvent=dojo.hitch(formWidget, formWidget.onSubmit);
						formWidget.onSubmit=function(){};
						evt=dojo.connect(formWidget, "onSubmit",dojo.hitch(self,event));
						if (dojo.config.isDebug) 
							console.debug("judo.zone: found form widget source "+formWidget.id);
					}
					else
					{
						node.__orgEvent=dojo.hitch(node, node.onsubmit);
						node.onsubmit="";
						evt=dojo.connect(node, "onsubmit",dojo.hitch(self,event));
						if (dojo.config.isDebug) 
							console.debug("judo.zone: found form source "+node.id);
					}
					break;
				default:
					node.__orgEvent=dojo.hitch(node, node.onclick);
					node.onclick="";
					evt=dojo.connect(node, "onclick", dojo.hitch(self,event));
					if (dojo.config.isDebug) 
						console.debug("judo.zone: found source "+node);
					
			}
		    _connects.push({e:evt,n:node});
		 });
	};
	
	function findConnectByNode(node)
	{
		for (var i=0;i<_connects.length;i++)
		{
			var c=_connects[i];
			if (c.n===node)
				return {c:c,i:i};
		}
	}
	
	this.connect=function(zoneId, event, handle)
	{
		var eh;
		var h;
		var e;
		function createEvents(){
			return new function(){
				this.onZoneBegin=function(/* String */ zoneId, /* domNode|widget */ zone){};
				this.onZoneSuccess=function(){};
				this.onZoneError=function(){};
				this.onZoneValidationError=function(){};
				this.onZoneEnd=function(){};
			};
		}
		if (arguments.length==3)
		{
			eh=_events[zoneId];
			e=event;
			h=handle;
			if (!eh)
			{
				eh=createEvents();
				_events[zoneId]=eh;
			}
		}
		else
		{
			e=zoneId;
			h=event;
			eh=_globalEvents;
			if (!eh)
			{
				eh=createEvents();
				_globalEvents=eh;
			}
		}
		return dojo.connect(eh,e,h);
	};
	
	this.activate=function(/* DomNode */ zoneSource)
	{
		// summary:
		//		activates zone source
		
		switch (zoneSource.tagName)
		{
			case "FORM":
				judo.events.simulate("submit",zoneSource);
				break;
			default:
				judo.events.simulate("click",zoneSource);
		}
	};

	
};

judo.addOnLoad(function(){
	judo.zone.scan();
});